﻿// Copyright 2013 Ronald Schlenker, www.technewlogic.de.
// 
// This file is part of Byond.
// 
// Byond is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
// 
// Byond is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with Byond.  If not, see <http://www.gnu.org/licenses/>.
using System;
using System.IO;
using System.Threading;
using NAudio.Wave;
using NAudio.Wave.SampleProviders;

namespace Byond.Builtin
{
	public class SampleBufferInternal : IDisposable
	{
		public SampleBufferInternal(
			string fileName,
			Action<SampleBufferInternal> loaded,
			Action<string> log)
			: this()
		{
			_fileName = fileName;
			_loaded = loaded;
			_log = log;
			_loadThread.Start();
		}

		private SampleBufferInternal()
		{
			_loadThread = new Thread(LoadSample)
			{
				IsBackground = true
			};

			Buffer2 = new double[0];
			Buffer1 = new double[0];
		}

		private readonly Thread _loadThread;
		private readonly string _fileName;
		
		private Action<SampleBufferInternal> _loaded;
		private Action<string> _log;
		private bool _loadThreadFinished;

		internal double[] Buffer1 { get; private set; }
		internal double[] Buffer2 { get; private set; }

		public long Length { get; private set; }
		public int Channels { get; private set; }

		public static SampleBufferInternal Empty
		{
			get { return new SampleBufferInternal(); }
		}

		private void LoadSample()
		{
			try
			{
				_log("Loading sample: " + _fileName);

				if (!File.Exists(_fileName))
				{
					_log("Sample not found: " + _fileName);
					return;
				}

				float[] sampleBuffer;

				using (
					var waveStream = Path.GetExtension(_fileName) == ".wav"
						? (WaveStream)new WaveFileReader(_fileName)
						: new Mp3FileReader(_fileName))
				{
					if (waveStream.WaveFormat.BitsPerSample != 16)
						throw new Exception("16bit required");
					if (waveStream.WaveFormat.Channels > 2)
						throw new Exception("1 or 2 channels required");
					Channels = waveStream.WaveFormat.Channels;

					using (var pcmStream = WaveFormatConversionStream.CreatePcmStream(waveStream))
					{
						var transformator = new Pcm16BitToSampleProvider(pcmStream);

						Length = pcmStream.Length / waveStream.WaveFormat.BlockAlign;
						sampleBuffer = new float[pcmStream.Length];
						transformator.Read(sampleBuffer, 0, sampleBuffer.Length);
					}
				}

				Buffer1 = new double[Length];
				Buffer2 = new double[Length];
				if (Channels == 1)
				{
					for (var i = 0; i < Length; i++)
					{
						// float to double conversion
						Buffer1[i] = Buffer2[i] = sampleBuffer[i];
					}
				}
				else
				{
					// Must be 2
					for (var i = 0; i < Length; i++)
					{
						Buffer1[i] = sampleBuffer[i * 2];
						Buffer2[i] = sampleBuffer[i * 2 + 1];
					}
				}

				_log("Sample loaded: " + _fileName);
				_loaded(this);
			}
			catch (ThreadAbortException)
			{
			}
			catch (Exception e)
			{
				_log("Could not load sample. " + e);
			}
			finally
			{
				lock (this)
					_loadThreadFinished = true;
			}
		}

		public void Dispose()
		{
			// IMP: Kann das hier ein Deadlock verursachen?
			lock (this)
			{
				// null: wegen .Empty
				if (!_loadThreadFinished)
					_loadThread.Abort();
			}

			_loaded = null;
			_log = null;

			// IMP: Ist das gut so?
			GC.Collect();
		}
	}
}